package apidoc

import (
	"fmt"
	"reflect"
	"strings"
)

type Document struct {
	Path     string
	Group    string
	Access   string
	Remark   string
	Extdata  string
	Request  string
	Response string
}

type DocumentList []Document

func (list DocumentList) Len() int {
	return len(list)
}

func (list DocumentList) Swap(i, j int) {
	list[i], list[j] = list[j], list[i]
}

func (list DocumentList) Less(i, j int) bool {
	return strings.Compare(list[i].Path, list[j].Path) < 0
}

var docmap = make(map[string]Document)

func replace(str, old, new string) string {
	return strings.Replace(str, old, new, -1)
}

func getString(clazz reflect.Type) string {
	doc := ""
	fieldnum := clazz.NumField()

	for i := 0; i < fieldnum; i++ {
		field := clazz.Field(i)
		item := FieldItem{}
		item.init(field)
		if field.Type.Kind() == reflect.Slice || field.Type.Kind() == reflect.Array {
			subclazz := field.Type.Elem()
			doc += fmt.Sprintf("\n<tr><td><span>%s</span></td><td>%s</td><td>%s</td><td>%s</td></tr>", item.name, "array", item.extdata, item.remark)

			if isObject(subclazz.Name()) {
				doc += replace(getString(subclazz), "\n<tr><td>", "\n<tr><td>&nbsp;&nbsp;<span>&gt;</span>")
			}
		} else if isObject(item.clazz) {
			doc += fmt.Sprintf("\n<tr><td><span>%s</span></td><td>%s</td><td>%s</td><td>%s</td></tr>", item.name, "object", item.extdata, item.remark)
			doc += replace(getString(field.Type), "\n<tr><td>", "\n<tr><td>&nbsp;&nbsp;<span>&gt;</span>")
		} else {
			doc += fmt.Sprintf("\n<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>", item.name, item.clazz, item.extdata, item.remark)
		}
	}

	for true {
		tmp := replace(doc, "<span>&gt;</span>&nbsp;&nbsp;", "<span>&nbsp;</span>&nbsp;&nbsp;")

		if len(tmp) == len(doc) {
			return doc
		}
		doc = tmp
	}

	return doc
}

func GetDocumentList() DocumentList {
	i := 0
	list := make([]Document, len(docmap))
	for _, v := range docmap {
		list[i] = v
		i++
	}
	return list
}

func GetDocument(path string) Document {
	res, succ := docmap[path]
	if succ {
		return res
	} else {
		if path[0] == '/' {
			return docmap[path[1:]]
		} else {
			return docmap["/"+path]
		}
	}
}

func AddDocument(request reflect.Type, response reflect.Type, args ...string) Document {
	doc := Document{}
	argc := len(args)

	if argc > 1 {
		doc.Access = args[1]
	}
	if argc > 2 {
		doc.Remark = args[2]
	}
	if argc > 3 {
		doc.Extdata = args[3]
	}

	doc.Path = args[0]
	doc.Request = getString(request)
	doc.Response = getString(response)

	docmap[args[0]] = doc

	return doc
}
